package global

import (
	"gitee.com/jamon/espoir_go/common"
	"gitee.com/jamon/espoir_go/common/utils"
	"gitee.com/jamon/espoir_go/logger"
	"sync"
)

type User = common.User

type UserManager struct {
	idSessionMap   utils.Map[uint64, string] // userID -> sessionID
	sessionUserMap utils.Map[string, uint64] // sessionID -> userID
	UserMap        utils.Map[uint64, *User]  // userID -> user
}

var (
	userMgr     *UserManager
	userMgrOnce sync.Once
)

func GetUserManager() *UserManager {
	userMgrOnce.Do(func() {
		userMgr = &UserManager{
			idSessionMap:   utils.NewMap[uint64, string](),
			sessionUserMap: utils.NewMap[string, uint64](),
			UserMap:        utils.NewMap[uint64, *User](),
		}
	})
	return userMgr
}

func (mgr *UserManager) AddUser(user *User) {
	mgr.UserMap.Put(user.UserID, user)
	mgr.idSessionMap.Put(user.UserID, user.SessionID)
	mgr.sessionUserMap.Put(user.SessionID, user.UserID)
}

func (mgr *UserManager) GetUserBySessionID(sessionID string) (user *User, exists bool) {
	userID, exists := mgr.sessionUserMap.Get(sessionID)
	if !exists {
		return nil, false
	}
	user, exists = mgr.UserMap.Get(userID)
	if !exists {
		return nil, false
	}
	return user, true
}

func (mgr *UserManager) GetUserByUserID(userID uint64) (user *User, exists bool) {
	user, exists = mgr.UserMap.Get(userID)
	if !exists {
		return nil, false
	}
	return user, true
}

func (mgr *UserManager) GetUserBySessionIDAndUserID(sessionID string, userID uint64) (user *User, exists bool) {
	user, exists = mgr.UserMap.Get(userID)
	if !exists {
		return nil, false
	}
	if user.SessionID != sessionID {
		return nil, false
	}
	return user, true
}

func (mgr *UserManager) RemoveUserBySessionID(sessionID string) {
	userID, exists := mgr.sessionUserMap.Get(sessionID)
	if !exists {
		return
	}
	mgr.idSessionMap.Remove(userID)
	mgr.sessionUserMap.Remove(sessionID)
	mgr.UserMap.Remove(userID)
}

func (mgr *UserManager) RemoveUserByUserID(userID uint64) {
	user, exists := mgr.UserMap.Get(userID)
	if !exists {
		return
	}
	mgr.idSessionMap.Remove(userID)
	mgr.sessionUserMap.Remove(user.SessionID)
	mgr.UserMap.Remove(userID)
}

func (mgr *UserManager) RemoveUserBySessionIDAndUserID(sessionID string, userID uint64) {
	user, exists := mgr.UserMap.Get(userID)
	if !exists {
		return
	}
	if user.SessionID != sessionID {
		return
	}
	mgr.idSessionMap.Remove(userID)
	mgr.sessionUserMap.Remove(sessionID)
	mgr.UserMap.Remove(userID)
}

func (mgr *UserManager) GetSessionIDByUserID(userID uint64) (sessionID string, exists bool) {
	sessionID, exists = mgr.idSessionMap.Get(userID)
	if !exists {
		return "", false
	}
	return sessionID, true
}

func (mgr *UserManager) GetSessionIDByUserIDs(userID []uint64) []*User {
	var users []*User
	for _, id := range userID {
		user, exists := mgr.UserMap.Get(id)
		if !exists {
			continue
		}
		users = append(users, user)
	}
	return users
}

func (mgr *UserManager) GetUserIDBySessionID(sessionID string) (userID uint64, exists bool) {
	userID, exists = mgr.sessionUserMap.Get(sessionID)
	if !exists {
		return 0, false
	}
	return userID, true
}

func (mgr *UserManager) UpdateUserSessionID(userID uint64, sessionID string) {
	mgr.idSessionMap.Put(userID, sessionID)
	mgr.sessionUserMap.Put(sessionID, userID)
	user, ok := mgr.UserMap.Get(userID)
	if !ok {
		logger.Error("UpdateUserSessionID: user not exists", logger.Kwargs{"user_id": userID})
		return
	}
	user.SessionID = sessionID
}
